//: [Previous](@previous)
protocol Drawable {
    func draw()
    var x: Int { get set } //x的声明, 需要满足get和set方法
    var y: Int { get } //y的声明, 需要满足get方法
    subscript(index: Int) -> Int { get set }
}
/*
  协议的定义
  1. 协议可以用来定义 方法, 属性, 下标 的声明, 协议可以被 枚举, 结构体, 类 遵守(多个协议之间用逗号隔开)
  protocol Test1 {}
  protocol Test2 {}
  protocol Test3 {}
  class TestClass: Test1, Test2, Test3
  //定义了3个协议, 定义了一个类遵循这3个协议
  
  2. 协议中定义方法时, 不能有默认参数值
  3. 默认情况下, 协议中定义的内容必须每个都实现
  */

//:## 协议中的属性
/*
 1. 协议中属性定义时,必须用var关键字
 2. 实现协议时的属性权限要大于协议中定义的属性权限
  2.1 协议定义get / set ,用var存储属性  或者 get / set计算属性去实现
      协议定义get, var的时候可以多用到set
  2.2 协议定义get, 用任何属性都可以实现,比如var,let
 */

class Person1 : Drawable {
    var x: Int = 0
    let y: Int = 0 //存储属性
    func draw() {
        print("func draw")
    }
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}

class Person2 : Drawable {
    var x: Int {//计算属性
        get { 0 }
        set { }
    }
    var y: Int { 0 }
    func draw() {
        print("func draw")
    }
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}
//:## static, class
//回忆知识点: class只能在类中修饰方法属性下标, 结构体和枚举不行. 但是协议是可以被结构体,类,枚举所继承的, 所以为了保证统一性, 就在协议中只能使用static去修饰方法类型下标.(class允许子类重写, static不允许子类重写)
//为了保证通用, 协议中必须用 static 定义 类型方法 / 类型属性 / 类型下标
protocol SC {
    static func test()
}
// 对象里面写static或者class取决于这个对象的子类要不要被重写
class sc1: SC {
    class func test() {}
}

class sc2: SC {
    static func test() {}
}

//:## mutating
/*
 只有将协议中的 实例方法 标记为mutating
 才允许结构体, 枚举 的具体实现时修改自身内存
 类在实现具体方法时哪怕协议是标记了mutating的也不用再加mutating, 只有枚举和结构体才需要这种情况再加mutating
 */
protocol mutatingP {
    mutating func test()
}
//因为类是引用类型, 内存本来就是可以修改
class muP1 : mutatingP {
    var width = 0
    func test() {
        width = 10
    }
}
struct muP2 : mutatingP {
    var width = 0
    mutating func test() {
        width = 10
    }
}
//:## init
/*
 协议中可以定义初始化器init
 非final类实现时,必须加上required
 知识点回忆:
 required : 用required修饰的指定初始化器(便捷初始化器没用), 表明其所有子类都必须实现该初始化器(通过自动继承或者重写实现))
 final : 不能再被继承和重写
 */
protocol initPro { // 其实就是要求所有遵循这个协议的都必须实现这个初始化器
    init(x: Int, y: Int)
}
class initPP : initPro {// 非final类实现时,必须加上required,这样子就可以保证这个类和它的子类都是必须实现了这个init
    required init(x: Int, y: Int) {
        
    }
}
final class initPPP : initPro {//final修饰的类不用加,因为没有子类了,这里就实现了就是唯一
    init(x: Int, y: Int) {
        
    }
}

// 如果从协议实现的初始化器, 刚好是重写了父类的指定初始化器, 那么这个初始化器必须同时加上required和override
protocol Livable {//一个协议,必须实现init
    init(age: Int)
}
class Person3 {//一个父类,指定初始化器
    init(age: Int) {}
}
//一个子类,继承自父类,也遵循了协议,
class student : Person3, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}//required是因为协议要必须实现,override是因为子类要实现父类的指定初始化器
//:## init, init?, init!
/*
 协议中定义的 init?, init!, 可以用init, init?, init! 去实现
 协议中定义的 init, 只可以用 init, init! 去实现
 ptotocol Livable {
    init() //情况1
    init?(age: Int) //情况2
    init!(no: Int) //情况3
 }
 class Person : Livable{
    情况1:
    required init() {}
    required init!() {}
 
    情况2:
    required init?(age: Int) {}
    required init(age: Int) {}
    required init!(age: Int) {}
 
    情况3:
    required init!(no: Int) {}
    required init(no: Int) {}
    required init?(no: Int) {}
 */
//: ## 协议的继承(遵循)
/*
 一个协议可以继承其他协议
 protocol Run { 第一个协议
    func run()
 }
 protocol jump : Run { 第二个协议,继承了第一个协议,这个协议里面还有第一个协议的方法声明
    func jump()
 }
 class Person : jump { 遵循了第二个协议的对象, 就要实现2个方法
    func run() {}
    func jump() {}
 }
 */
//: ## 协议组合
//协议组合, 可以由多个协议或者多加最多1个的类组合, 只能类不能是结构体或者枚举
protocol protocolA {}
protocol protocolB {}
class PersonClass {}
// 接收PersonClass或者其子类的实例
func fn0(obj : PersonClass) {}
// 接收遵守协议protocolB的实例
func fn1(obj : protocolB) {}
// 接收同时遵守protocolA 和 protocolB协议的实例
func fn2(obj : protocolA & protocolB) {}
// 接收同时遵守protocolA 和 protocolB协议, 并且是PersonClass或者其子类的实例
func fn2(obj : PersonClass & protocolA & protocolB) {}
//用别名代替,优化观感
typealias RealP = PersonClass & protocolA & protocolB
func fn4(obj : RealP) {}
//:## CaseIterable
// 让枚举遵守caseIterable协议, 可以实现遍历枚举值
enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
let seasons = Season.allCases
print(seasons.count)
for season in seasons {
    print(season)
}
//spring
//summer
//autumn
//winter
//:## CustomStringConvertible
// 遵守costomStringConvertible协议, 可以自定义实例的打印字符串, 相当于oc的对象.decription
class CCSc : CustomStringConvertible {
    var age: Int = 0
    var description: String {
        "age = \(age), 我想打印什么就是什么"
    }
}
var cc = CCSc()
print(cc)//age = 0, 我想打印什么就是什么
//: ## Any, AnyObject *重点知识
/*
 swift提供了2种特殊的类型: Any, AnyObject
 Any: 可以代表任意类型 (枚举 / 结构体 / 类 / 也包括函数类型)
 AnyObject: 可以代表任何 类的类型 (在协议后面写上:AnyObject 代表只有类能遵守这个协议)
 */
var stu : Any = 0 //这里=0,所以stu是int类型了
stu = "string" //这里=0,所以stu是string类型了, 因为定义的时候用了Any
stu = CCSc() //怎么搞都不会报错
//举例需求: 创建一个能存放任意类型的数组
var data = [Any]()
//var data = Array<Any>() 这个和上一行一样的效果
data.append(1)
data.append(3.14)
data.append(CCSc())
data.append({10})//闭包表达式
data.append("生日")
print(data) //[1, 3.14, age = 0, 我想打印什么就是什么, (Function), "生日"]
//: ## is, as?, as!, as *重点知识点
// is 用来判断是否为某种类型
protocol Runnable { func run() } //创建一个协议
class person4 {}
class student4 : person4, Runnable { //创建一个对象,继承了类person4, 遵守了协议Runnable
    func run() {//实现了协议的方法
        print("student4 run")
    }
    func study() {//student4 自己的方法
        print("student4 study")
    }
}

var stu4: Any = 10
print(stu4 is Int) //true
stu4 = "Jack"
print(stu4 is String) //true
print(stu4 is Runnable) //false
print(stu4 is person4) //false
print(stu4 is student4) //false
stu4 = student4() //任意类型都可以判断,协议啊,类啊什么的都可以
print(stu4 is Runnable) //true
print(stu4 is person4) //true
print(stu4 is student4) //true

//as? as! 用来做强制类型转换, 因为转换基本都是不能百分百保证能转换的, 所以都是用可选项 ? 或者 !
(stu4 as? student4)?.study() //调用成功, 第一个?:因为不能确定强制转换会不会成功,所以要返回可选类型 第二个?:因为前面的是可选类型,要使用对象的方法,就需要加?
(stu4 as! student4).study() //调用成功, !是强制解包,这种是有风险的,一旦失败就会奔溃
(stu4 as? Runnable)?.run() //调用成功

//as 只有在百分百能转换成功的情况下,才可以使用
var dataArr = [Any]()
dataArr.append(Int("123") as Any) //Int("123")这里是不一定转换成功的,所以是返回了可选项,但是可选项转换成any是一定成功的,所以就可以使用as
dataArr.append(321 as Any) //同理,一切都可以转换成any

var d = 10 as Double //10转Double是肯定成功
print(d) //10.0
//: ## X.self / X.Type / AnyClass
/*
 X.self 是一个元类型(metadata)的指针, metadata存放着类型相关的信息
 X.self 属于X.Type类型
 */
class Person5 { } //定义一个类
class Student5 : Person5 { } //定义一个类,继承person
var perType: Person5.Type = Person5.self
var stuType: Student5.Type = Student5.self
perType = stuType.self
//.self其实就是.Type类型,Student5是继承自Person5, 所以也能赋值到父类

var anyType: AnyObject.Type = Person5.self //AnyObject是任意类,所以就包含了Person5,Student5
anyType = stuType.self // anyType = 任何类型.self

// AnyClass : 系统的 public typealias AnyClass = any AnyObject.Type, 其实就是所以类的type的集合的别名
var anyType2: AnyClass = Person5.self
anyType2 = Student5.self

// type(of: xxx) //获取这个对象的类型, 就是取出对象的内存中的前八个字节(存放类型信息的指针地址)
var typeTemp = type(of: Student5()) //也可以传入具体对象
print(typeTemp) //Student5
print(Student5.self == type(of: Student5())) //true
//:## 元类型的应用
class Animal { required init(){} }
class Cat : Animal {}
class Dog : Animal {}
class Pig : Animal {}
//元类型,可以直接使用required的指定初始化器
func create(_ clses: [Animal.Type]) -> [Animal] { //根据传入的类的类型信息的元类型,初始化生成对象
    var arr = [Animal]()
    for cls in clses {
        arr.append(cls.init())
    }
    return arr
}
print(create([Cat.self, Dog.self, Pig.self]))
//: ## Self 大写的self
// Self一般作为返回值类型使用, 限定返回值跟方法调用者必须同一类型 (某些少的情况下可以作为参数类型)
// 如果Self是用在 类 中, 就要求返回时调用的初始化器是带required的,因为是要用元类型去调用所以一定要required
protocol TestProtocolSelf {
    func test() -> Self
}
class penClass : TestProtocolSelf {
    required init() {}
    func test() -> Self {
        type(of: self).init() //利用自身的元类型去调用init初始化
    }
}
class Student6 : penClass {} //继承上面
var ss = penClass()
print(ss.test()) //penClass 返回自己的类型
var sss = Student6()
print(sss.test()) //Student6 返回自己的类型
/*
 Self 代表当前类型
 */
class PersonSelf {
    var age = 10
    static var count = 2
    func test() {
        print(self.age)//小写的self,代表的是实例,test()是实例方法,所以当前谁在调用这个方法,self就是谁.实例对象只能访问实例属性,所以self只能访问age
        print(Self.count)//大写的Seld代表是当前的类型,当前是代表PersonSelf这个类型,Self可以访问当前类的类型属性或者类型方法
    }
}


//: ## CustomStringConvertible / CustomDebugStringConvertible
/*
 遵守CustomStringConvertible / CustomDebugStringConvertible协议, 都可以自定义实例的打印字符串内容
 2者区别不大,仅有的一点小区别👇🏻:
 1.print调用的是CustomStringConvertible协议的description
 2.debugPrint / 打断点时候控制台的输入po 调用的是CustomDebugStringConvertible协议的debugDescription
 */
class PersonCC : CustomStringConvertible, CustomDebugStringConvertible {
    var age = 10
    var description: String { "PersonCC_\(age)" }
    var debugDescription: String { "Debug_PersonCC_\(age)" }
}
var pcc = PersonCC()
pcc.description//PersonCC_10
pcc.debugDescription//Debug_PersonCC_10



//: [Next](@next)
